//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#include "BindingSitePredictionRefinement.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <math.h>

#include <fstream>
#include <iostream>
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
using namespace std;
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
#define MAX_SIZE       500000
#define USE_FACTOR_POW 2
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
namespace BindingSiteRefinement {
  
  bool EqualHeaders (TNode <PredictBindingSiteByPattern>* predictBindingSite, void* ref) {
    
    PredictBindingSiteByPattern* predictBindingSiteRef = (PredictBindingSiteByPattern*)ref;

    return (!strcmp(predictBindingSite->Info()->Header()->Out(), predictBindingSiteRef->Header()->Out()))?true:false;

  }
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
BindingSitePredictionRefinement::BindingSitePredictionRefinement () {

  this->filenameOut                        = new String ();
  this->predictionBindingSite              = new BindingSitePredictionData ();
  this->predictionBindingSiteRefined       = new BindingSitePredictionData ();
  this->refinementBindingSites             = new TListE <BindingSitePredictionData> ();
  this->refinementLingoSimilarity          = new TListE <float> ();
  this->toString                           = new String ();

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
BindingSitePredictionRefinement::BindingSitePredictionRefinement (const BindingSitePredictionRefinement& bindingSitePredictionRefinement) {

  this->filenameOut                        = new String (bindingSitePredictionRefinement.filenameOut);
  this->predictionBindingSite              = new BindingSitePredictionData (bindingSitePredictionRefinement.predictionBindingSite);
  this->predictionBindingSiteRefined       = new BindingSitePredictionData (bindingSitePredictionRefinement.predictionBindingSiteRefined);
  this->refinementBindingSites             = new TListE <BindingSitePredictionData> (bindingSitePredictionRefinement.refinementBindingSites);
  this->refinementLingoSimilarity          = new TListE <float> (bindingSitePredictionRefinement.refinementLingoSimilarity);
  this->toString                           = new String (bindingSitePredictionRefinement.toString);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
BindingSitePredictionRefinement::BindingSitePredictionRefinement (const BindingSitePredictionRefinement* bindingSitePredictionRefinement) {

  this->filenameOut                        = new String (bindingSitePredictionRefinement->filenameOut);
  this->predictionBindingSite              = new BindingSitePredictionData (bindingSitePredictionRefinement->predictionBindingSite);
  this->predictionBindingSiteRefined       = new BindingSitePredictionData (bindingSitePredictionRefinement->predictionBindingSiteRefined);
  this->refinementBindingSites             = new TListE <BindingSitePredictionData> (bindingSitePredictionRefinement->refinementBindingSites);
  this->refinementLingoSimilarity          = new TListE <float> (bindingSitePredictionRefinement->refinementLingoSimilarity);
  this->toString                           = new String (bindingSitePredictionRefinement->toString);

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
BindingSitePredictionRefinement::~BindingSitePredictionRefinement () {

  if (this->filenameOut)                     delete this->filenameOut;
  if (this->predictionBindingSite)           delete this->predictionBindingSite;
  if (this->predictionBindingSiteRefined)    delete this->predictionBindingSiteRefined;
  if (this->refinementBindingSites)          delete this->refinementBindingSites;
  if (this->refinementLingoSimilarity)       delete this->refinementLingoSimilarity;
  if (this->toString)                        delete this->toString;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void BindingSitePredictionRefinement::FilenameOut (String* filenameOut) {

  if (filenameOut)
    *(this->filenameOut) = *filenameOut;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void BindingSitePredictionRefinement::PredictionBindingSite (BindingSitePredictionData* predictionBindingSite) {

  if (predictionBindingSite)
    *(this->predictionBindingSite) = *predictionBindingSite;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void BindingSitePredictionRefinement::PredictionBindingSiteRefined (BindingSitePredictionData* predictionBindingSiteRefined) {

  if (predictionBindingSiteRefined)
    *(this->predictionBindingSiteRefined) = *predictionBindingSiteRefined;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void BindingSitePredictionRefinement::RefinementBindingSites (TListE<BindingSitePredictionData>* refinementBindingSites) {

  if (refinementBindingSites)
    *(this->refinementBindingSites) = *refinementBindingSites;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
void BindingSitePredictionRefinement::RefinementLingoSimilarity (TListE<float>* refinementLingoSimilarity) {

  if (refinementLingoSimilarity)
    *(this->refinementLingoSimilarity) = *refinementLingoSimilarity;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* BindingSitePredictionRefinement::FilenameOut (void) {

  return this->filenameOut;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
BindingSitePredictionData* BindingSitePredictionRefinement::PredictionBindingSite (void) {

  return this->predictionBindingSite;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
BindingSitePredictionData* BindingSitePredictionRefinement::PredictionBindingSiteRefined (void) {

  return this->predictionBindingSiteRefined;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE<BindingSitePredictionData>* BindingSitePredictionRefinement::RefinementBindingSites (void) {

  return this->refinementBindingSites;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
TListE<float>* BindingSitePredictionRefinement::RefinementLingoSimilarity (void) {

  return this->refinementLingoSimilarity;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool BindingSitePredictionRefinement::LoadData (void) {

  if (!this->predictionBindingSite                         || !this->predictionBindingSite->FilenameIn() || 
      !this->predictionBindingSite->FilenameIn()->Length() || !this->refinementBindingSites              || 
      !this->refinementBindingSites->Length()              || !this->refinementLingoSimilarity           ||
      !this->refinementLingoSimilarity->Length()           || this->refinementBindingSites->Length() != this->refinementLingoSimilarity->Length())
    return false;
  
  if (!this->predictionBindingSite->LoadData())
    return false;
  
  this->predictionBindingSiteRefined->FilenameIn(this->predictionBindingSite->FilenameIn());
  
  if (!this->predictionBindingSiteRefined->LoadData())
    return false;
  
  this->refinementBindingSites->SetInitial();
  while (this->refinementBindingSites->SetNext())
    if (!this->refinementBindingSites->GetCurrent()->LoadData())
      return false;
  
  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool BindingSitePredictionRefinement::ComputeRefinePredictionBindingSite (int useFactor) {

  if (!this->refinementBindingSites          || !this->refinementBindingSites->Length()                    ||
      !this->refinementLingoSimilarity       || !this->refinementLingoSimilarity->Length()                 ||
      !this->predictionBindingSite           || !this->predictionBindingSite->ListPredictionBS()->Length() ||
      !this->predictionBindingSiteRefined    || !this->predictionBindingSiteRefined->ListPredictionBS() ||
      this->refinementBindingSites->Length() !=  this->refinementLingoSimilarity->Length())
    return false;
  
  if (useFactor == USE_FACTOR_AS_SUM)
    this->predictionBindingSiteRefined->CleanPredictions(0);
  
  float distanceBetweenPicks = 0;
  float refinementFactor     = 0;
  float predictionRefined    = 0;
  
  this->refinementBindingSites->SetInitial();
  this->refinementLingoSimilarity->SetInitial();

  while (this->refinementBindingSites->SetNext() && this->refinementLingoSimilarity->SetNext()) {
    
    BindingSitePredictionData* currentBindingSitePredictionData = this->refinementBindingSites->GetCurrent();
    float                      lingoSim                         = *(this->refinementLingoSimilarity->GetCurrent());
    
    this->predictionBindingSiteRefined->ListPredictionBS()->SetInitial();
    while (this->predictionBindingSiteRefined->ListPredictionBS()->SetNext()) {
      
      PredictBindingSiteByPattern* currentPredictBindingSiteRefinement = this->predictionBindingSiteRefined->ListPredictionBS()->GetCurrent();
      
      PredictBindingSiteByPattern* searchPredictBindingSite            = this->predictionBindingSite->ListPredictionBS()->FirstOcurrence(BindingSiteRefinement::EqualHeaders, currentPredictBindingSiteRefinement);
      PredictBindingSiteByPattern* searchPredictBindingSiteRefinement  = currentBindingSitePredictionData->ListPredictionBS()->FirstOcurrence(BindingSiteRefinement::EqualHeaders, currentPredictBindingSiteRefinement);
      
      if (!searchPredictBindingSite || !searchPredictBindingSiteRefinement) {
        cout << "Error!!! Not found the header: " << currentPredictBindingSiteRefinement->Header()->Out() << endl;
        continue;
      }
      
      for (int i = 0; i < currentPredictBindingSiteRefinement->Sequence()->Length(); i++) {
        
        distanceBetweenPicks = fabs(((searchPredictBindingSite->ScoreAA())[i]) - ((searchPredictBindingSiteRefinement->ScoreAA())[i]));
        refinementFactor     = pow(fabs(fabs(pow((1 - lingoSim), 2) - pow((distanceBetweenPicks), 2)) - 1), USE_FACTOR_POW);
        predictionRefined    = refinementFactor * ((searchPredictBindingSite->ScoreAA())[i]);
        
        if      (useFactor == USE_FACTOR_AS_SUM)
          ((currentPredictBindingSiteRefinement->ScoreAA())[i]) += predictionRefined;
        else if (useFactor == USE_FACTOR_AS_PRODUCT)
          ((currentPredictBindingSiteRefinement->ScoreAA())[i]) *= refinementFactor;
        
        distanceBetweenPicks = fabs(((searchPredictBindingSite->ScoreCH())[i]) - ((searchPredictBindingSiteRefinement->ScoreCH())[i]));
        refinementFactor     = pow(fabs(fabs(pow((1 - lingoSim), 2) - pow((distanceBetweenPicks), 2)) - 1), USE_FACTOR_POW);
        predictionRefined    = refinementFactor * ((searchPredictBindingSite->ScoreCH())[i]);
        
        if      (useFactor == USE_FACTOR_AS_SUM)
          ((currentPredictBindingSiteRefinement->ScoreCH())[i]) += predictionRefined;
        else if (useFactor == USE_FACTOR_AS_PRODUCT)
          ((currentPredictBindingSiteRefinement->ScoreCH())[i]) *= refinementFactor;
        
        distanceBetweenPicks = fabs(((searchPredictBindingSite->ScorePH())[i]) - ((searchPredictBindingSiteRefinement->ScorePH())[i]));
        refinementFactor     = pow(fabs(fabs(pow((1 - lingoSim), 2) - pow((distanceBetweenPicks), 2)) - 1), USE_FACTOR_POW);
        predictionRefined    = refinementFactor * ((searchPredictBindingSite->ScorePH())[i]);
        
        if      (useFactor == USE_FACTOR_AS_SUM)
          ((currentPredictBindingSiteRefinement->ScorePH())[i]) += predictionRefined;
        else if (useFactor == USE_FACTOR_AS_PRODUCT)
          ((currentPredictBindingSiteRefinement->ScorePH())[i]) *= refinementFactor;
        
        distanceBetweenPicks = fabs(((searchPredictBindingSite->ScoreSS())[i]) - ((searchPredictBindingSiteRefinement->ScoreSS())[i]));
        refinementFactor     = pow(fabs(fabs(pow((1 - lingoSim), 2) - pow((distanceBetweenPicks), 2)) - 1), USE_FACTOR_POW);
        predictionRefined    = refinementFactor * ((searchPredictBindingSite->ScoreSS())[i]);
        
        if      (useFactor == USE_FACTOR_AS_SUM)
          ((currentPredictBindingSiteRefinement->ScoreSS())[i]) += predictionRefined;
        else if (useFactor == USE_FACTOR_AS_PRODUCT)
          ((currentPredictBindingSiteRefinement->ScoreSS())[i]) *= refinementFactor;
        
        distanceBetweenPicks = fabs(((searchPredictBindingSite->ScoreMT())[i]) - ((searchPredictBindingSiteRefinement->ScoreMT())[i]));
        refinementFactor     = pow(fabs(fabs(pow((1 - lingoSim), 2) - pow((distanceBetweenPicks), 2)) - 1), USE_FACTOR_POW);
        predictionRefined    = refinementFactor * ((searchPredictBindingSite->ScoreMT())[i]);
        
        if      (useFactor == USE_FACTOR_AS_SUM)
          ((currentPredictBindingSiteRefinement->ScoreMT())[i]) += predictionRefined;
        else if (useFactor == USE_FACTOR_AS_PRODUCT)
          ((currentPredictBindingSiteRefinement->ScoreMT())[i]) *= refinementFactor;
        
        distanceBetweenPicks = fabs(((searchPredictBindingSite->ScoreCumul())[i]) - ((searchPredictBindingSiteRefinement->ScoreCumul())[i]));
        refinementFactor     = pow(fabs(fabs(pow((1 - lingoSim), 2) - pow((distanceBetweenPicks), 2)) - 1), USE_FACTOR_POW);
        predictionRefined    = refinementFactor * ((searchPredictBindingSite->ScoreCumul())[i]);
        
        if      (useFactor == USE_FACTOR_AS_SUM)
          ((currentPredictBindingSiteRefinement->ScoreCumul())[i]) += predictionRefined;
        else if (useFactor == USE_FACTOR_AS_PRODUCT)
          ((currentPredictBindingSiteRefinement->ScoreCumul())[i]) *= refinementFactor;
        
      }
      
    }
    
  }

  if (useFactor == USE_FACTOR_AS_SUM) {
    
    this->predictionBindingSiteRefined->ListPredictionBS()->SetInitial();
    while (this->predictionBindingSiteRefined->ListPredictionBS()->SetNext()) {

      PredictBindingSiteByPattern* currentPredictBindingSiteRefinement = this->predictionBindingSiteRefined->ListPredictionBS()->GetCurrent();

      for (int i = 0; i < currentPredictBindingSiteRefinement->Sequence()->Length(); i++) {

        ((currentPredictBindingSiteRefinement->ScoreAA())[i])    /= this->refinementBindingSites->Length();
        ((currentPredictBindingSiteRefinement->ScoreCH())[i])    /= this->refinementBindingSites->Length();
        ((currentPredictBindingSiteRefinement->ScorePH())[i])    /= this->refinementBindingSites->Length();
        ((currentPredictBindingSiteRefinement->ScoreSS())[i])    /= this->refinementBindingSites->Length();
        ((currentPredictBindingSiteRefinement->ScoreMT())[i])    /= this->refinementBindingSites->Length();
        ((currentPredictBindingSiteRefinement->ScoreCumul())[i]) /= this->refinementBindingSites->Length();

      }

    }
    
  }

  return true;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String* BindingSitePredictionRefinement::ToCSVScores (void) {

  int type = 1;
  
  this->toString->In("");
  
  this->predictionBindingSiteRefined->ListPredictionBS()->SetInitial();
  while (this->predictionBindingSiteRefined->ListPredictionBS()->SetNext()) {
    
    PredictBindingSiteByPattern* currentPredictBindingSiteByPattern = this->predictionBindingSiteRefined->ListPredictionBS()->GetCurrent();
    
    this->toString->Add(currentPredictBindingSiteByPattern->Header());
    this->toString->Add("\n");
    this->toString->Add(this->OutFormatedArray(currentPredictBindingSiteByPattern->Sequence(), type).Out());
    this->toString->Add("\n");
    this->toString->Add(this->OutFormatedArray(currentPredictBindingSiteByPattern->ScoreAA(),        currentPredictBindingSiteByPattern->Sequence()->Length(), type).Out());
    this->toString->Add("\n");
    this->toString->Add(this->OutFormatedArray(currentPredictBindingSiteByPattern->ScoreCH(),        currentPredictBindingSiteByPattern->Sequence()->Length(), type).Out());
    this->toString->Add("\n");
    this->toString->Add(this->OutFormatedArray(currentPredictBindingSiteByPattern->ScorePH(),        currentPredictBindingSiteByPattern->Sequence()->Length(), type).Out());
    this->toString->Add("\n");
    this->toString->Add(this->OutFormatedArray(currentPredictBindingSiteByPattern->ScoreSS(),        currentPredictBindingSiteByPattern->Sequence()->Length(), type).Out());
    this->toString->Add("\n");
    this->toString->Add(this->OutFormatedArray(currentPredictBindingSiteByPattern->ScoreMT(),        currentPredictBindingSiteByPattern->Sequence()->Length(), type).Out());
    this->toString->Add("\n");
    this->toString->Add(this->OutFormatedArray(currentPredictBindingSiteByPattern->ScoreCumul(),     currentPredictBindingSiteByPattern->Sequence()->Length(), type).Out());
    this->toString->Add("\n");
    this->toString->Add(this->OutFormatedArray(currentPredictBindingSiteByPattern->AnchorResidues(), currentPredictBindingSiteByPattern->Sequence()->Length(), type).Out());
    this->toString->Add("\n");
    
  }
  
  return this->toString;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String BindingSitePredictionRefinement::OutFormatedArray (String* data, int type) {
  
  String out;
  char   buffer [MAX_SIZE];
  
  out.In("");
  if (data)
    for (long i = 0; i < data->Length(); i++) {
      if (!type) sprintf(buffer, "%c      \0", data->Out()[i]);
      else       sprintf(buffer, (!i)?"%c\0":";%c\0", data->Out()[i]);
      out.Add(buffer);
    }
    
  return out;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
String BindingSitePredictionRefinement::OutFormatedArray (float* data, int size, int type) {
  
  String out;
  char   buffer [MAX_SIZE];
  
  out.In("");
  if (data)
    for (long i = 0; i < size; i++) {
      if (!type) sprintf(buffer, "%3.2f \0", data[i]);
      else       sprintf(buffer, (!i)?"%3.2f\0":";%3.2f\0", data[i]);
      out.Add(buffer);
    }
    
  return out;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
bool BindingSitePredictionRefinement::OutToCSVFile (void) {

  if (!this->filenameOut || !this->filenameOut->Length())
    return false;
  
  ofstream file (this->filenameOut->Out());
  if (!file.is_open())
    return false;
  file << this->ToCSVScores()->Out();
  file.close();
  
  return true;
  
}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
BindingSitePredictionRefinement BindingSitePredictionRefinement::operator= (const BindingSitePredictionRefinement& bindingSitePredictionRefinement) {

  *(this->filenameOut)                     = *(bindingSitePredictionRefinement.filenameOut);
  *(this->predictionBindingSite)           = *(bindingSitePredictionRefinement.predictionBindingSite);
  *(this->predictionBindingSiteRefined)    = *(bindingSitePredictionRefinement.predictionBindingSiteRefined);
  *(this->refinementBindingSites)          = *(bindingSitePredictionRefinement.refinementBindingSites);
  *(this->refinementLingoSimilarity)       = *(bindingSitePredictionRefinement.refinementLingoSimilarity);
  *(this->toString)                        = *(bindingSitePredictionRefinement.toString);

  return *this;

}
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
//---------------------------------------------------------------------------------------------------------------------------------------------------------//
